/**
 * 
 */
package com.vivid.dynamics.oknow.test.dao;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.vivid.dynamics.junit.impl.SpringHibernateSupportJUnitTemplate;
import com.vivid.dynamics.oknow.dao.IBookmarkDAO;
import com.vivid.dynamics.oknow.dao.IUserDAO;
import com.vivid.dynamics.oknow.model.Bookmark;
import com.vivid.dynamics.oknow.model.User;

/**
 *
 * @author jason
 * created at:: Aug 9, 2011 12:00:57 PM
 */
public class BookmarkTestCase extends SpringHibernateSupportJUnitTemplate {
	/**
	 * IBookmarkDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IBookmarkDAO bookmarkDAO;
	/**
	 * IUserDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IUserDAO userDAO;
	
	/**
	 * ctor.
	 */
	public BookmarkTestCase() {
		super(BookmarkTestCase.class, true);
	}
	
	User userA=null;
	User userB=null;
	Serializable userAKey=null;
	Serializable userBKey=null;
	
	/**
	 * method(s) to create 2 default users for testing
	 * 
	 * @author jason
	 * created at:: Aug 9, 2011  12:06:58 PM
	 */
	protected void createDefaultUsers() {
		userA=new User("userA", "def", false, "hire", "a@abcAA.com", null);
		userB=new User("userB", "def", false, "hire", "a@abcBB.com", null);
		
		this.userAKey=this.userDAO.newUser(userA);
		this.userBKey=this.userDAO.newUser(userB);
	}
	
	/**
	 * method(s) to remove the 2 default users for testing
	 * 
	 * @author jason
	 * created at:: Aug 9, 2011  12:07:34 PM
	 * @deprecated
	 */
	protected void removeDefaultUsers() {
		if (this.userA!=null) {
			for (Bookmark aBk:this.userA.getBookmarks()) {
				this.userA.removeBookmark(aBk);
				this.bookmarkDAO.deleteBookmark(aBk);
			}
			this.userDAO.updateUser(this.userA);
			this.userDAO.deleteUser(userA);
		}
		//if (this.userB!=null) this.userDAO.deleteUser(userB);
		if (this.userB!=null) {
			for (Bookmark aBk:this.userB.getBookmarks()) {
				this.userB.removeBookmark(aBk);
				this.bookmarkDAO.deleteBookmark(aBk);
			}
			this.userDAO.updateUser(this.userB);
			this.userDAO.deleteUser(userB);
		}
		this.userAKey=null;
		this.userBKey=null;
	}
	
	/**
	 * <ul>
	 * 	<li>create 2 User(s) {userA and userB}, and create some bookmark entries for them (2 per user)</li>
	 * 	<li>pdate the user's bookmark(s)</li>
	 * 	<li>change ownership of bookmark from UserA to UserB</li>
	 * </ul>
	 * @author jason
	 * created at:: Aug 9, 2011  12:04:58 PM
	 */
	@Test	
	public void doTest001() {
		User aUsrA=null;
		User aUsrB=null;
		Bookmark aMarkA01=null;
		Bookmark aMarkA02=null;
		Bookmark aMarkB01=null;
		Bookmark aMarkB02=null;
		Timestamp aCurDate=new Timestamp(new Date().getTime());
		
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {BookmarkTestCase.doTest001}\t*\r\n").
			append("************************************************\r\n\r\n");
		
		try {
			this.createDefaultUsers();
			
			aMarkA01=new Bookmark("science bk A1", "just a short bookmark description later will try that 3000 content and trimming~", aCurDate, 0, 0, 0d, 0, 0, 0d, "science;fiction;totally odd", true, "http://www.google.com/q=science");
			aMarkA02=new Bookmark("comics A2", "jojo bizarre adventures\t\tdragonBall", aCurDate, 0, 0, 0d, 0, 0, 0d, "comcis;manga;jojo;goku", true, "http://www.google.com/q=manga");
			aMarkB01=new Bookmark("movie trilogies B1", "bookmark on movies", aCurDate, 0, 0, 0d, 0, 0, 0d, "movie;drama;fantasy", true, "http://www.google.com/q=movies");
			aMarkB02=new Bookmark("samuria B2", "bookmark on samurai~~", aCurDate, 0, 0, 0d, 0, 0, 0d, "sword;blade", true, "http://www.google.com/q=samurai");
			/*
			 *	a) new users (giving them id)
			 *	b) add the bookmark associations
			 *	c) new bookmarks (giving them id) 
			 */
			aUsrA=this.userA;
			aUsrB=this.userB;
			/*
			 * bookmark is a List under User; therefore no hashCode() hell problem~
			 * no need to clear, then re-add back to the collection
			 */
			aUsrA.addBookmark(aMarkA01);
			aUsrA.addBookmark(aMarkA02);
			aUsrB.addBookmark(aMarkB01);
			aUsrB.addBookmark(aMarkB02);			
			this.bookmarkDAO.newBookmark(aMarkA01);
			this.bookmarkDAO.newBookmark(aMarkA02);
			this.bookmarkDAO.newBookmark(aMarkB01);
			this.bookmarkDAO.newBookmark(aMarkB02);
			
			// re-query
			aUsrA=this.userDAO.getUserByKey((Integer) this.userAKey);
			aUsrB=this.userDAO.getUserByKey((Integer) this.userBKey);
			Assert.assertTrue("1) both users should be logically the same~ UsrA", aUsrA.equals(this.userA));
			Assert.assertTrue("1) both users should be logically the same~ UsrB", aUsrB.equals(this.userB));
			Assert.assertTrue("1) UsrA should have 2 bookmarks~", aUsrA.getBookmarks().size()==2);
			Assert.assertTrue("1) UsrB should have 2 bookmarks~", aUsrB.getBookmarks().size()==2);

			this.sb.append(aUsrA).append("\r\n\t").append(aUsrA.getBookmarks()).append("\r\n\t\t").
				append(aUsrB).append("\r\n\t").append(aUsrB.getBookmarks()).append("\r\n\r\n");
			
			/*
			 * 2) update the user's bookmark(s)
			 */
			this.sb.append("2) update the user's bookmark(s); prepending the date on the description field~~~\r\n\t");
			Assert.assertTrue("2) UsrA should have exactly 2 bookmarks", aUsrA.getBookmarks()!=null && aUsrA.getBookmarks().size()==2);
			aMarkA01=aUsrA.getBookmarks().get(0);
			aMarkA02=aUsrA.getBookmarks().get(1);
			aMarkA01.setDescr(aCurDate+":::"+aMarkA01.getDescr());
			aMarkA02.setDescr(aCurDate+":::"+aMarkA02.getDescr());
			
			Assert.assertTrue("2) UsrB should have exactly 2 bookmarks", aUsrB.getBookmarks()!=null && aUsrB.getBookmarks().size()==2);
			aMarkB01=aUsrB.getBookmarks().get(0);
			aMarkB02=aUsrB.getBookmarks().get(1);
			aMarkB01.setDescr(aCurDate+"###"+aMarkB01.getDescr());
			aMarkB02.setDescr(aCurDate+"###"+aMarkB02.getDescr());
			/*
			 * should update on the Owner side
			 */
			this.userDAO.updateUser(aUsrA);
			this.userDAO.updateUser(aUsrB);
			
			// re-query
			aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n");
			this.createToStringForCollections(this.sb, aUsrA.getBookmarks(), 1);
			this.sb.append("\r\n\t\t").append(aUsrB).append(" :: \r\n");
			this.createToStringForCollections(this.sb, aUsrB.getBookmarks(), 1);
			this.sb.append("\r\n\r\n{Bookmarks} ::\r\n\t\t").
				append(aMarkA01).append(" :: \r\n").append(aMarkA01.getUser()).append("\r\n\t\t").
				append(aMarkA02).append(" :: \r\n").append(aMarkA02.getUser()).append("\r\n\t\t").
				append(aMarkB01).append(" :: \r\n").append(aMarkB01.getUser()).append("\r\n\t\t").
				append(aMarkB02).append(" :: \r\n").append(aMarkB02.getUser()).append("\r\n\r\n");
			
			/*
			 * 3) change ownership of bookmark from UserA to UserB 
			 */
			this.sb.append("3) change ownership of bookmark from UserA to UserB; shift BookmarkA01 to userB::\r\n\t");
			aUsrA.removeBookmark(aMarkA02);
			aUsrB.addBookmark(aMarkA02);
			/*
			 *  single side update should be OK
			 *  remember to use association methods~ or else... will have 
			 *  	unexpected EXCEPTIONS 
			 */
			this.userDAO.updateUser(aUsrA);
			this.userDAO.updateUser(aUsrB);
			
			// re-query
			aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n");
			this.createToStringForCollections(this.sb, aUsrA.getBookmarks(), 1);
			this.sb.append("\r\n\t\t").append(aUsrB).append(" :: \r\n");
			this.createToStringForCollections(this.sb, aUsrB.getBookmarks(), 1);
			this.sb.append("\r\n\r\n{Bookmarks} ::\r\n\t\t").
				append(aMarkA01).append(" :: \r\n").append(aMarkA01.getUser()).append("\r\n\t\t").
				append(aMarkA02).append(" :: \r\n").append(aMarkA02.getUser()).append("\r\n\t\t").
				append(aMarkB01).append(" :: \r\n").append(aMarkB01.getUser()).append("\r\n\t\t").
				append(aMarkB02).append(" :: \r\n").append(aMarkB02.getUser()).append("\r\n\r\n");
			
			//this.removeDefaultUsers();
			
			this.tx.flush();
			
		} catch(RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
	/**
	 * 
	 * Delete of bookmark from UserA
	 * 
	 * @author jason
	 * created at:: Aug 9, 2011  12:29:56 PM
	 */
	@Test
	public void doTest002() {
		User aUsrA=null;
		User aUsrB=null;
		Bookmark aMarkA01=null;
		Bookmark aMarkA02=null;
		Bookmark aMarkB01=null;
		Bookmark aMarkB02=null;		
		Timestamp aCurDate=new Timestamp(new Date().getTime());
				
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {BookmarkTestCase.doTest002}\t*\r\n").
			append("************************************************\r\n\r\n");
		
		try {
			this.createDefaultUsers();
			
			aMarkA01=new Bookmark("science bk A1", "just a short bookmark description later will try that 3000 content and trimming~", aCurDate, 0, 0, 0d, 0, 0, 0d, "science;fiction;totally odd", true, "http://www.google.com/q=science");
			aMarkA02=new Bookmark("comics A2", "jojo bizarre adventures\t\tdragonBall", aCurDate, 0, 0, 0d, 0, 0, 0d, "comcis;manga;jojo;goku", true, "http://www.google.com/q=manga");
			aMarkB01=new Bookmark("movie trilogies B1", "bookmark on movies", aCurDate, 0, 0, 0d, 0, 0, 0d, "movie;drama;fantasy", true, "http://www.google.com/q=movies");
			aMarkB02=new Bookmark("samuria B2", "bookmark on samurai~~", aCurDate, 0, 0, 0d, 0, 0, 0d, "sword;blade", true, "http://www.google.com/q=samurai");
			/*
			 *	a) new users (giving them id)
			 *	b) add the bookmark associations
			 *	c) new bookmarks (giving them id) 
			 */
			aUsrA=this.userA;
			aUsrB=this.userB;
			/*
			 * bookmark is a List under User; therefore no hashCode() hell problem~
			 * no need to clear, then re-add back to the collection
			 */
			aUsrA.addBookmark(aMarkA01);
			aUsrA.addBookmark(aMarkA02);
			aUsrB.addBookmark(aMarkB01);
			aUsrB.addBookmark(aMarkB02);			
			this.bookmarkDAO.newBookmark(aMarkA01);
			this.bookmarkDAO.newBookmark(aMarkA02);
			this.bookmarkDAO.newBookmark(aMarkB01);
			this.bookmarkDAO.newBookmark(aMarkB02);
			
			// re-query
			aUsrA=this.userDAO.getUserByKey((Integer) this.userAKey);
			aUsrB=this.userDAO.getUserByKey((Integer) this.userBKey);
			Assert.assertTrue("1) both users should be logically the same~ UsrA", aUsrA.equals(this.userA));
			Assert.assertTrue("1) both users should be logically the same~ UsrB", aUsrB.equals(this.userB));
			Assert.assertTrue("1) UsrA should have 2 bookmarks~", aUsrA.getBookmarks().size()==2);
			Assert.assertTrue("1) UsrB should have 2 bookmarks~", aUsrB.getBookmarks().size()==2);

			this.sb.append(aUsrA).append("\r\n\t");
			this.createToStringForCollections(this.sb, aUsrA.getBookmarks(), 1);
			this.sb.append("\r\n\t\t").append(aUsrB).append("\r\n\t");
			this.createToStringForCollections(this.sb, aUsrB.getBookmarks(), 1);
			this.sb.append("\r\n\r\n");
			
			/*
			 * 4) Delete of bookmark from UserA ; use associations
			 */
			this.sb.append("4) Delete of bookmark from UserA ; use associations; remove the last bookmark from UserB~\r\n\t");
			Assert.assertTrue("4) userA should have exactly 2 bookmarks~", aUsrA.getBookmarks()!=null && aUsrA.getBookmarks().size()==2);
			Assert.assertTrue("4) userB should have exactly 2 bookmarks~", aUsrB.getBookmarks()!=null && aUsrB.getBookmarks().size()==2);
			this.sb.append("*** the bookmark that should be removed is :: ").append(aMarkB01).append("\r\n");
			aUsrB.removeBookmark(aMarkB01);
			this.bookmarkDAO.deleteBookmark(aMarkB01);
			this.userDAO.updateUser(aUsrB);
			
			// re-query
			aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n");
			if (aUsrA!=null) { this.createToStringForCollections(this.sb, aUsrA.getBookmarks()); }
			this.sb.append("\r\n\t\t").append(aUsrB).append(" :: \r\n");
			if (aUsrB!=null) { this.createToStringForCollections(this.sb, aUsrB.getBookmarks()); }
			this.sb.append("\r\n\r\n{Bookmarks} ::\r\n\t\t").append(aMarkA01).append(" :: \r\n");
			if (aMarkA01!=null) {this.sb.append(aMarkA01.getUser()).append("\r\n\t\t");} 
			this.sb.append(aMarkA02).append(" :: \r\n");
			if (aMarkA02!=null) {this.sb.append(aMarkA02.getUser()).append("\r\n\t\t");}
			this.sb.append(aMarkB01).append(" :: \r\n");
			if (aMarkB01!=null) {this.sb.append(aMarkB01.getUser()).append("\r\n\t\t");}
			this.sb.append(aMarkB02).append(" :: \r\n");
			if (aMarkB02!=null) {this.sb.append(aMarkB02.getUser()).append("\r\n\r\n"); }
			
			this.tx.flush();
			
		} catch(RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
	/**
	 * Delete of UserB, check whether bookmarks go orphaned
	 * <p/>
	 * <b>PS</b>. deliberately not THROW the RuntimeException~ 
	 * 	since the result will die due to "no delete action on cascade"
	 * 
	 * @author jason
	 * created at:: Aug 9, 2011  3:30:40 PM
	 */
	@Test
	public void doTest003() {
		User aUsrA=null;
		User aUsrB=null;
		Bookmark aMarkA01=null;
		Bookmark aMarkA02=null;
		Bookmark aMarkB01=null;
		Bookmark aMarkB02=null;		
		Timestamp aCurDate=new Timestamp(new Date().getTime());
				
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {BookmarkTestCase.doTest003}\t*\r\n").
			append("************************************************\r\n\r\n");
		
		try {
			this.createDefaultUsers();
			
			aMarkA01=new Bookmark("science bk A1", "just a short bookmark description later will try that 3000 content and trimming~", aCurDate, 0, 0, 0d, 0, 0, 0d, "science;fiction;totally odd", true, "http://www.google.com/q=science");
			aMarkA02=new Bookmark("comics A2", "jojo bizarre adventures\t\tdragonBall", aCurDate, 0, 0, 0d, 0, 0, 0d, "comcis;manga;jojo;goku", true, "http://www.google.com/q=manga");
			aMarkB01=new Bookmark("movie trilogies B1", "bookmark on movies", aCurDate, 0, 0, 0d, 0, 0, 0d, "movie;drama;fantasy", true, "http://www.google.com/q=movies");
			aMarkB02=new Bookmark("samuria B2", "bookmark on samurai~~", aCurDate, 0, 0, 0d, 0, 0, 0d, "sword;blade", true, "http://www.google.com/q=samurai");
			/*
			 *	a) new users (giving them id)
			 *	b) add the bookmark associations
			 *	c) new bookmarks (giving them id) 
			 */
			aUsrA=this.userA;
			aUsrB=this.userB;
			/*
			 * bookmark is a List under User; therefore no hashCode() hell problem~
			 * no need to clear, then re-add back to the collection
			 */
			aUsrA.addBookmark(aMarkA01);
			aUsrA.addBookmark(aMarkA02);
			aUsrB.addBookmark(aMarkB01);
			aUsrB.addBookmark(aMarkB02);			
			this.bookmarkDAO.newBookmark(aMarkA01);
			this.bookmarkDAO.newBookmark(aMarkA02);
			this.bookmarkDAO.newBookmark(aMarkB01);
			this.bookmarkDAO.newBookmark(aMarkB02);
			
			// re-query
			aUsrA=this.userDAO.getUserByKey((Integer) this.userAKey);
			aUsrB=this.userDAO.getUserByKey((Integer) this.userBKey);
			Assert.assertTrue("1) both users should be logically the same~ UsrA", aUsrA.equals(this.userA));
			Assert.assertTrue("1) both users should be logically the same~ UsrB", aUsrB.equals(this.userB));
			Assert.assertTrue("1) UsrA should have 2 bookmarks~", aUsrA.getBookmarks().size()==2);
			Assert.assertTrue("1) UsrB should have 2 bookmarks~", aUsrB.getBookmarks().size()==2);

			this.sb.append(aUsrA).append("\r\n\t");
			this.createToStringForCollections(this.sb, aUsrA.getBookmarks(), 1);
			this.sb.append("\r\n\t\t").append(aUsrB).append("\r\n\t");
			this.createToStringForCollections(this.sb, aUsrB.getBookmarks(), 1);
			this.sb.append("\r\n\r\n");
			
			/*
			 * 5) Delete of UserB, check whether bookmarks go orphaned
			 */
			this.sb.append("5) Delete of UserB, check whether bookmarks go orphaned {without association methods!}::\r\n\t").
				append("FAIL for sure~~~ don't ever remove entities without using Association methods~~~~\r\n\r\n");
			this.userDAO.deleteUser(aUsrB);
			
			// re-query
			aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n");
			if (aUsrA!=null) { this.createToStringForCollections(this.sb, aUsrA.getBookmarks()); }
			this.sb.append("\r\n\t\t").append(aUsrB).append(" :: \r\n");
			if (aUsrB!=null) { this.createToStringForCollections(this.sb, aUsrB.getBookmarks()); }
			this.sb.append("\r\n\r\n{Bookmarks} ::\r\n\t\t").append(aMarkA01).append(" :: \r\n");
			if (aMarkA01!=null) {this.sb.append(aMarkA01.getUser()).append("\r\n\t\t");} 
			this.sb.append(aMarkA02).append(" :: \r\n");
			if (aMarkA02!=null) {this.sb.append(aMarkA02.getUser()).append("\r\n\t\t");}
			this.sb.append(aMarkB01).append(" :: \r\n");
			if (aMarkB01!=null) {this.sb.append(aMarkB01.getUser()).append("\r\n\t\t");}
			this.sb.append(aMarkB02).append(" :: \r\n");
			if (aMarkB02!=null) {this.sb.append(aMarkB02.getUser()).append("\r\n\r\n"); }
			
			this.tx.flush();
			
		} catch(RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			//throw re;
		}
	}
	
	/**
	 * Test(s) on User and Bookmark associations
	 * <ul>
	 * 	<li></li>
	 * 	<li></li>
	 * 	<li></li>
	 * 	<li></li>
	 * 	<li></li>
	 * </ul>
	 * <b>PS</b>. bookmark n permission tests are performed in another JUnit method
	 * <p/>
	 * @author jason
	 * created at:: Aug 4, 2011  12:18:10 PM
	 * @deprecated
	 */
	//@Test
	public void doUserNBookmarkTest001() {
		User aUsrA=null;
		User aUsrB=null;
		Bookmark aMarkA01=null;
		Bookmark aMarkA02=null;
		Bookmark aMarkB01=null;
		Bookmark aMarkB02=null;
		Bookmark aMarkDelete=null;
		Serializable aKeyUsrA=null;
		Serializable aKeyUsrB=null;
		//Serializable aKeyMarkA=null;
		//Serializable aKeyMarkB=null;
		Timestamp aCurDate=new Timestamp(new Date().getTime());
		
		try {
			this.sb.append("\r\n************************************************\r\n").
			    append("*\tstart {doUserNBookmarkTest001}\t*\r\n").
				append("************************************************\r\n\r\n");
			this.sb.append("1) create 2 User(s) {userA and userB}, and create some bookmark entries for them (2 per user):\r\n\tUsers::\r\n\t\t");
			aUsrA=new User("usrA", "def", false, "h->a", "a@abcm.com", null);
			aUsrB=new User("usrB", "def", false, "h->a->90", "a@abcm.com", "fantom@blood.com");

			aMarkA01=new Bookmark("science bk A1", "just a short bookmark description later will try that 3000 content and trimming~", aCurDate, 0, 0, 0d, 0, 0, 0d, "science;fiction;totally odd", true, "http://www.google.com/q=science");
			aMarkA02=new Bookmark("comics A2", "jojo bizarre adventures\r\ndragonBall", aCurDate, 0, 0, 0d, 0, 0, 0d, "comcis;manga;jojo;goku", true, "http://www.google.com/q=manga");
			aMarkB01=new Bookmark("movie trilogies B1", "bookmark on movies", aCurDate, 0, 0, 0d, 0, 0, 0d, "movie;drama;fantasy", true, "http://www.google.com/q=movies");
			aMarkB02=new Bookmark("samuria B2", "bookmark on samurai~~", aCurDate, 0, 0, 0d, 0, 0, 0d, "sword;blade", true, "http://www.google.com/q=samurai");
			/*
			 *	a) new users (giving them id)
			 *	b) add the bookmark associations
			 *	c) new bookmarks (giving them id) 
			 */
			aKeyUsrA=this.userDAO.newUser(aUsrA);
			aKeyUsrB=this.userDAO.newUser(aUsrB);
			aUsrA.addBookmark(aMarkA01);
			aUsrA.addBookmark(aMarkA02);
			aUsrB.addBookmark(aMarkB01);
			aUsrB.addBookmark(aMarkB02);			
			this.bookmarkDAO.newBookmark(aMarkA01);
			this.bookmarkDAO.newBookmark(aMarkA02);
			this.bookmarkDAO.newBookmark(aMarkB01);
			this.bookmarkDAO.newBookmark(aMarkB02);
			
			// re-query
			aUsrA=this.userDAO.getUserByKey((Integer) aKeyUsrA);
			aUsrB=this.userDAO.getUserByKey((Integer) aKeyUsrB);
			this.sb.append(aUsrA).append("\r\n\t").append(aUsrA.getBookmarks()).append("\r\n\t\t").
				append(aUsrB).append("\r\n\t").append(aUsrB.getBookmarks()).append("\r\n\r\n");
			
			/*
			 * 2) update the user's bookmark(s)
			 */
			this.sb.append("2) update the user's bookmark(s); prepending the date on the description field~~~\r\n\t");
			if (aUsrA.getBookmarks()!=null && aUsrA.getBookmarks().size()==2) {
				aMarkA01=aUsrA.getBookmarks().get(0);
				aMarkA02=aUsrA.getBookmarks().get(1);
				
				aMarkA01.setDescr(aCurDate+":::"+aMarkA01.getDescr());
				aMarkA02.setDescr(aCurDate+":::"+aMarkA02.getDescr());
			}
			if (aUsrB.getBookmarks()!=null && aUsrB.getBookmarks().size()==2) {
				aMarkB01=aUsrB.getBookmarks().get(0);
				aMarkB02=aUsrB.getBookmarks().get(1);
				
				aMarkB01.setDescr(aCurDate+":::"+aMarkB01.getDescr());
				aMarkB02.setDescr(aCurDate+":::"+aMarkB02.getDescr());
			}
			/*
			 * single side update is OK now;
			 * contrasting with "new" case; both ends need a "new" operation
			 */
			this.bookmarkDAO.updateBookmark(aMarkA01);
			this.bookmarkDAO.updateBookmark(aMarkA02);
			this.bookmarkDAO.updateBookmark(aMarkB01);
			this.bookmarkDAO.updateBookmark(aMarkB02);
			
			// re-query
			aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).
				append("\r\n\t\t").append(aUsrB).append("\r\n\r\n{Bookmarks} ::\r\n\t\t").
				append(aMarkA01).append("\r\n\t\t").
				append(aMarkA02).append("\r\n\t\t").
				append(aMarkB01).append("\r\n\t\t").
				append(aMarkB02).append("\r\n\r\n");
			
			/*
			 * 3) change ownership of bookmark from UserA to UserB 
			 */
			this.sb.append("3) change ownership of bookmark from UserA to UserB; shift BookmarkA01 to userB::\r\n\t");
			aUsrA.removeBookmark(aMarkA02);
			aUsrB.addBookmark(aMarkA02);
			/*
			 *  single side update should be OK
			 *  remember to use association methods~ or else... will have 
			 *  	unexpected EXCEPTIONS 
			 */
			this.userDAO.updateUser(aUsrA);
			this.userDAO.updateUser(aUsrB);
			
			// re-query
			aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n").append(aUsrA.getBookmarks()).
				append("\r\n\t\t").append(aUsrB).append(" :: \r\n").append(aUsrB.getBookmarks()).
				append("\r\n\r\n{Bookmarks} ::\r\n\t\t").
				append(aMarkA01).append(" :: \r\n").append(aMarkA01.getUser()).append("\r\n\t\t").
				append(aMarkA02).append(" :: \r\n").append(aMarkA02.getUser()).append("\r\n\t\t").
				append(aMarkB01).append(" :: \r\n").append(aMarkB01.getUser()).append("\r\n\t\t").
				append(aMarkB02).append(" :: \r\n").append(aMarkB02.getUser()).append("\r\n\r\n");
			
			/*
			 * 4) Delete of bookmark from UserA ; use associations
			 */
			this.sb.append("4) Delete of bookmark from UserA ; use associations; remove the last bookmark from UserB~\r\n\t");
			if (aUsrB.getBookmarks()!=null && aUsrB.getBookmarks().size()>0) {
				aMarkDelete=aUsrB.getBookmarks().get(aUsrB.getBookmarks().size()-1);
				aUsrB.removeBookmark(aMarkDelete);
				this.bookmarkDAO.deleteBookmark(aMarkDelete);
				this.userDAO.updateUser(aUsrB);
				
				// re-query
				aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
				aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
				aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
				aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
				aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
				aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
				
				this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n");
				if (aUsrA!=null) { this.sb.append(aUsrA.getBookmarks()); }
				this.sb.append("\r\n\t\t").append(aUsrB).append(" :: \r\n");
				if (aUsrB!=null) { this.sb.append(aUsrB.getBookmarks()); }
				this.sb.append("\r\n\r\n{Bookmarks} ::\r\n\t\t").append(aMarkA01).append(" :: \r\n");
				if (aMarkA01!=null) {this.sb.append(aMarkA01.getUser()).append("\r\n\t\t");} 
				this.sb.append(aMarkA02).append(" :: \r\n");
				if (aMarkA02!=null) {this.sb.append(aMarkA02.getUser()).append("\r\n\t\t");}
				this.sb.append(aMarkB01).append(" :: \r\n");
				if (aMarkB01!=null) {this.sb.append(aMarkB01.getUser()).append("\r\n\t\t");}
				this.sb.append(aMarkB02).append(" :: \r\n");
				if (aMarkB02!=null) {this.sb.append(aMarkB02.getUser());}
				
				this.sb.append("\r\n\r\n");
			}
			
			/*
			 * 5) Delete of UserB, check whether bookmarks go orphaned
			 */
			this.sb.append("5) Delete of UserB, check whether bookmarks go orphaned {without association methods!}::\r\n\t").
				append("FAIL for sure~~~ don't ever remove entities without using Association methods~~~~\r\n\r\n");
			/*this.userDAO.deleteUser(aUsrB);
			
			// re-query
			if (aMarkA01!=null) aMarkA01=this.bookmarkDAO.getBookmarkByKey(aMarkA01.getId());
			if (aMarkA02!=null) aMarkA02=this.bookmarkDAO.getBookmarkByKey(aMarkA02.getId());
			if (aMarkB01!=null) aMarkB01=this.bookmarkDAO.getBookmarkByKey(aMarkB01.getId());
			if (aMarkB02!=null) aMarkB02=this.bookmarkDAO.getBookmarkByKey(aMarkB02.getId());
			if (aUsrA!=null) aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			if (aUsrB!=null) aUsrB=this.userDAO.getUserByKey(aUsrB.getId());
			
			this.sb.append("{Users} :: \r\n\t\t").append(aUsrA).append(" :: \r\n");
			if (aUsrA!=null) { this.sb.append(aUsrA.getBookmarks()); }
			this.sb.append("\r\n\t\t").append(aUsrB).append(" :: \r\n");
			if (aUsrB!=null) { this.sb.append(aUsrB.getBookmarks()); }
			this.sb.append("\r\n\r\n{Bookmarks} ::\r\n\t\t").append(aMarkA01).append(" :: \r\n");
			if (aMarkA01!=null) {this.sb.append(aMarkA01.getUser()).append("\r\n\t\t");} 
			this.sb.append(aMarkA02).append(" :: \r\n");
			if (aMarkA02!=null) {this.sb.append(aMarkA02.getUser()).append("\r\n\t\t");}
			this.sb.append(aMarkB01).append(" :: \r\n");
			if (aMarkB01!=null) {this.sb.append(aMarkB01.getUser()).append("\r\n\t\t");}
			this.sb.append(aMarkB02).append(" :: \r\n");
			if (aMarkB02!=null) {this.sb.append(aMarkB02.getUser());}
			
			this.sb.append("\r\n\r\n");*/
			
			this.tx.flush();
			
		} catch(RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
}
